package yatan.common;

import java.io.FileInputStream;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import yatan.cluster.NodeConfigurationException;

public class ConfigUtility {
    private ConfigUtility() {
    }

    public static Properties loadConfigFile(String configFile) {
        // load configuration file
        Properties properties = new Properties();
        InputStream is = ConfigUtility.class.getClassLoader().getResourceAsStream(configFile);
        try {
            // if the file cannot be found in class path, try it from file system
            if (is == null) {
                is = new FileInputStream(configFile);
            }

            properties.load(is);
        } catch (FileNotFoundException e) {
            throw new NodeConfigurationException("Cannot find configuration file: " + configFile, e);
        } catch (IOException e) {
            throw new NodeConfigurationException("Cannot read configuration file: " + configFile + ". IO error: "
                    + e.getMessage());
        }

        return properties;
    }

    public static String readStringProperty(Properties properties, String key, boolean required, String defaultValue) {
        try {
            String value = (String) properties.get(key);
            if (value == null || value.trim().length() == 0) {
                if (required) {
                    throw new NodeConfigurationException("Required property " + key
                            + " does not exist of is empty in configuration.");
                }

                return defaultValue;
            }

            return value;
        } catch (ClassCastException e) {
            throw new NodeConfigurationException("The property " + key + " must be a string.");
        }
    }

    public static int readIntProperty(Properties properties, String key, boolean required, int defaultValue) {
        try {
            return Integer.parseInt(readStringProperty(properties, key, required, Integer.toString(defaultValue)));
        } catch (NumberFormatException e) {
            throw new NodeConfigurationException("The property " + key + " must be an integer.");
        }
    }

    public static <T> T createObject(Properties properties, String key, Class<T> expectedType) {
        String className = readStringProperty(properties, key, true, null);
        try {
            return expectedType.cast(Class.forName(className).newInstance());
        } catch (InstantiationException e) {
            throw new NodeConfigurationException("Cannot create object with the key " + key + ". Instantiation error: "
                    + e.getMessage(), e);
        } catch (IllegalAccessException e) {
            throw new NodeConfigurationException("Cannot create object with the key " + key + ". Illegal access: "
                    + e.getMessage(), e);
        } catch (ClassNotFoundException e) {
            throw new NodeConfigurationException("Cannot create object with the key " + key + ". The class "
                    + className + " can not be found.", e);
        } catch (ClassCastException e) {
            throw new NodeConfigurationException("Cannot create object with the key " + key + ". The object of type "
                    + className + " is not an instance of " + expectedType, e);
        }
    }
}
